package com.itwxz.forum.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itwxz.common.constant.CacheConstants;
import com.itwxz.common.constant.UserConstants;
import com.itwxz.common.core.redis.RedisCache;
import com.itwxz.common.exception.ServiceException;
import com.itwxz.common.exception.user.CaptchaException;
import com.itwxz.common.exception.user.CaptchaExpireException;
import com.itwxz.common.utils.StringUtils;
import com.itwxz.forum.domain.dto.UpdateForumUserDto;
import com.itwxz.forum.domain.po.*;
import com.itwxz.forum.domain.vo.ForumUserInfoVo;
import com.itwxz.forum.mapper.ForumUserMapper;
import com.itwxz.forum.mapper.ForumUserUpdateLogMapper;
import com.itwxz.forum.service.ForumCommentChildService;
import com.itwxz.forum.service.ForumCommentParentService;
import com.itwxz.forum.service.ForumPostService;
import com.itwxz.forum.service.ForumUserService;
import com.itwxz.forum.utils.SaUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
@Transactional
public class ForumUserServiceImpl extends ServiceImpl<ForumUserMapper, ForumUser> implements ForumUserService {
    @Autowired
    private ForumUserMapper userMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ForumUserUpdateLogMapper updateLogMapper;
    @Autowired
    private ForumPostService postService;
    @Autowired
    private ForumCommentParentService commentParentService;
    @Autowired
    private ForumCommentChildService commentChildService;

    @Override
    public ForumUser selectUserByUserName(String username) {
        return userMapper.selectUserByUserName(username);
    }


    /**
     * 查询论坛用户信息列表
     *
     * @param forumUser 论坛用户信息
     * @return 论坛用户信息
     */
    @Override
    public List<ForumUser> selectForumUserList(ForumUser forumUser) {
        return userMapper.selectForumUserList(forumUser);
    }

    /**
     * 校验用户名称是否唯一
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public boolean checkUserNameUnique(ForumUser user) {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();

        ForumUser info = getOne(
                new LambdaQueryWrapper<ForumUser>()
                        .eq(ForumUser::getUserName, user.getUserName()),
                false // 防止多条时抛出异常
        );

        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验手机号码是否唯一
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public boolean checkPhoneUnique(ForumUser user) {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();

        ForumUser info = getOne(
                new LambdaQueryWrapper<ForumUser>()
                        .eq(ForumUser::getPhoneNumber, user.getPhoneNumber()),
                false // 防止多条时抛出异常
        );

        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验email是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkEmailUnique(ForumUser user) {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();

        ForumUser info = getOne(
                new LambdaQueryWrapper<ForumUser>()
                        .eq(ForumUser::getEmail, user.getEmail()),
                false // 防止多条时抛出异常
        );

        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验user是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkUserUnique(ForumUser user) {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();

        LambdaQueryWrapper<ForumUser> qw = new LambdaQueryWrapper<>();
        qw.eq(ForumUser::getEmail, user.getEmail())
                .or().eq(ForumUser::getUserName, user.getUserName())
                .or().eq(ForumUser::getPhoneNumber, user.getPhoneNumber());

        ForumUser info = getOne(qw, false);

        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 更新用户信息
     *
     * @param dto
     * @return
     */
    @Override
    public String updateForumUser(UpdateForumUserDto dto) {
        validateCaptcha(dto.getCode(), dto.getUuid());

        ForumUserInfoVo userInfo = SaUtils.getForumUser();
        if (userInfo == null) {
            throw new RuntimeException("用户未登录或会话已过期");
        }

        Long userId = userInfo.getUserId();
        String oldNickName = userInfo.getUser().getNickName();
        String newNickName = dto.getNickName();

        // 如果昵称有变动，检查修改次数限制
        if (!Objects.equals(oldNickName, newNickName)) {
            checkUserNickNameUpdateLimit(userId);
        }

        ForumUser forumUser = BeanUtil.copyProperties(dto, ForumUser.class);
        forumUser.setUserId(userId);

        // 更新数据库中的用户信息
        boolean updated = updateById(forumUser);
        if (!updated) {
            return "用户信息更新失败";
        }

        // 如果昵称有变更，做后续更新和日志记录
        if (!Objects.equals(oldNickName, newNickName)) {
            // 更新帖子表中的昵称
            LambdaUpdateWrapper<ForumPost> postUpdateWrapper = new LambdaUpdateWrapper<>();
            postUpdateWrapper.eq(ForumPost::getUserId, userId)
                    .set(ForumPost::getCreateBy, newNickName);
            postService.update(postUpdateWrapper);

            // 更新评论表中的昵称
            LambdaUpdateWrapper<ForumCommentParent> commentPUpdateWrapper = new LambdaUpdateWrapper<>();
            commentPUpdateWrapper.eq(ForumCommentParent::getUserId, userId)
                    .set(ForumCommentParent::getCreateBy, newNickName);
            commentParentService.update(commentPUpdateWrapper);

            LambdaUpdateWrapper<ForumCommentChild> commentCUpdateWrapper = new LambdaUpdateWrapper<>();
            commentCUpdateWrapper.eq(ForumCommentChild::getUserId, userId)
                    .set(ForumCommentChild::getCreateBy, newNickName);
            commentChildService.update(commentCUpdateWrapper);

            // 更新会话缓存中的昵称
            userInfo.getUser().setNickName(newNickName);
            StpUtil.getSession().set("forum-user", userInfo);

            // 插入修改日志记录
            ForumUserUpdateLog updateLog = new ForumUserUpdateLog();
            updateLog.setUserId(userId);
            updateLog.setUpdateTime(LocalDateTime.now());
            updateLog.setUpdateField("nickName");
            updateLog.setRemark("用户修改昵称为：" + newNickName);
            updateLogMapper.insert(updateLog);
        }

        return "更新成功";
    }


    /**
     * 校验邮箱验证码
     *
     * @param code 验证码
     * @param uuid 唯一标识
     */
    public void validateCaptcha(String code, String uuid) {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
        String captcha = redisCache.getCacheObject(verifyKey);

        if (captcha == null) throw new CaptchaExpireException();

        redisCache.deleteObject(verifyKey);

        if (!code.equalsIgnoreCase(captcha)) throw new CaptchaException();
    }

    private void checkUserNickNameUpdateLimit(Long userId) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime monthStart = now.withDayOfMonth(1).with(LocalTime.MIN);
        LocalDateTime monthEnd = now.withDayOfMonth(now.toLocalDate().lengthOfMonth()).with(LocalTime.MAX);

        long updateCount = updateLogMapper.selectCount(
                new LambdaQueryWrapper<ForumUserUpdateLog>()
                        .eq(ForumUserUpdateLog::getUserId, userId)
                        .eq(ForumUserUpdateLog::getUpdateField, "nickName")
                        .between(ForumUserUpdateLog::getUpdateTime, monthStart, monthEnd)
        );

        if (updateCount >= 2) {
            throw new RuntimeException("本月昵称只能修改2次，请下月再试！");
        }
    }

}
